package cn.mrcode.wxsdk.core.dialogue.common.util;

import com.google.zxing.*;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zhuqiang
 * @version V1.0
 * @Description: 谷歌家的二维码工具类
 * @date 2015/8/25 9:03
 */
public class XZingUtil {
    private static Logger log = LoggerFactory.getLogger(XZingUtil.class);

    /**
     * 为二维码图片增加logo头像
     * 其原理类似于图片加水印,图片叠加
     *
     * @param imagePath 二维码图片存放路径(含文件名)
     * @param logoPath  logo头像存放路径(含文件名)
     */
    private static void overlapImage(String imagePath, String logoPath) throws IOException {
        BufferedImage image = ImageIO.read(new File(imagePath));
        int logoWidth = image.getWidth() / 5;   //设置logo图片宽度为二维码图片的五分之一
        int logoHeight = image.getHeight() / 5; //设置logo图片高度为二维码图片的五分之一
        int logoX = (image.getWidth() - logoWidth) / 2;   //设置logo图片的位置,这里令其居中
        int logoY = (image.getHeight() - logoHeight) / 2; //设置logo图片的位置,这里令其居中
        Graphics2D graphics = image.createGraphics();
        graphics.drawImage(ImageIO.read(new File(logoPath)), logoX, logoY, logoWidth, logoHeight, null);
        graphics.dispose();
        ImageIO.write(image, imagePath.substring(imagePath.lastIndexOf(".") + 1), new File(imagePath));
    }

    /**
     * 为二维码图片增加logo头像
     * 其原理类似于图片加水印,图片叠加
     * @param bufferedImage
     * @param mediaType 图片后缀（如   png）
     * @param logoPath logo图片绝对位置
     * @param outputStream  合成好之后，放入的流中
     * @throws IOException
     */
    private static void overlapImageToStream(BufferedImage bufferedImage, String mediaType, String logoPath, OutputStream outputStream) throws IOException {
        // 如果加上logo图片。不能被微信扫描给识别。可能就是这里的logo图片太大了。
        int logoWidth = bufferedImage.getWidth() / 6;   //设置logo图片宽度为二维码图片的6分之一
        int logoHeight = bufferedImage.getHeight() / 6; //设置logo图片高度为二维码图片的6分之一
        int logoX = (bufferedImage.getWidth() - logoWidth) / 2;   //设置logo图片的位置,这里令其居中
        int logoY = (bufferedImage.getHeight() - logoHeight) / 2; //设置logo图片的位置,这里令其居中
        Graphics2D graphics = bufferedImage.createGraphics();
        graphics.drawImage(ImageIO.read(new File(logoPath)), logoX, logoY, logoWidth, logoHeight, null);
        graphics.dispose();
        ImageIO.write(bufferedImage, mediaType, outputStream);
    }

    /**
     * 生成二维码，带logo 图片的，在本类的解析下将会失败（logo图片太大会造成解析失败）
     *
     * @param content    二维码内容，内容越少。图片矩阵越少
     * @param charset    编码二维码内容时采用的字符集(传null时默认采用UTF-8编码)
     * @param folderPath 二维码图片存放路径(绝对路径目录如：d:/img/)
     * @param imageName  二维码图片名称(包含后缀，如：123.png)
     * @param width      生成的二维码图片宽度
     * @param height     生成的二维码图片高度
     * @param logoPath   logo头像存放路径(含文件名,若不加logo则传null即可)
     *
     * @return 生成二维码结果(true or false)
     */
    public static boolean encodeQRCodeImage(String content, String charset, String folderPath, String imageName, int width, int height, String logoPath) {
        String format = imageName.substring(imageName.lastIndexOf(".") + 1);// 图像类型
        Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
        if (charset == null) {
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");  //设置内容编码
        } else {
            hints.put(EncodeHintType.CHARACTER_SET, charset);  //设置内容编码
        }

        try {
            //先保证图片路径目录 已经存在
            File folder = new File(folderPath);
            if (!folder.exists()) {
                boolean mkdirs = folder.mkdirs();
                if(!mkdirs){
                    throw  new RuntimeException("创建目录失败：" + folder.getAbsolutePath());
                }
            }
            // 生成矩阵
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
            Path path = FileSystems.getDefault().getPath(folderPath, imageName);
            //生成的二维码图片默认背景为白色,前景为黑色,但是在加入logo图像后会导致logo也变为黑白色,至于是什么原因还未知
            //所以这里对其第一个参数黑色将ZXing默认的前景色0xFF000000稍微改了一下0xFF000001,最终效果也是白色背景黑色前景的二维码,且logo颜色保持原有不变
            MatrixToImageConfig config = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
            //这里要显式指定MatrixToImageConfig,否则还会按照默认处理将logo图像也变为黑白色(如果打算加logo的话,反之则不须传MatrixToImageConfig参数)
            MatrixToImageWriter.writeToPath(bitMatrix, format, path, config);// 输出图像
            log.info("二维码生成成功：" + path);
            if (logoPath != null) {
                overlapImage(path.toString(), logoPath);
            }
            return true;
        } catch (Exception e) {
            log.info("二维码生成失败");
            e.printStackTrace();
        }

        return false;
    }

    /**
     * 生成二维码
     *
     * @param content    二维码内容，内容越少。图片矩阵越少
     * @param charset    编码二维码内容时采用的字符集(传null时默认采用UTF-8编码)
     * @param mediaType  生成的二维码图片格式。如 png
     * @param width      生成的二维码图片宽度
     * @param height     生成的二维码图片高度
     *
     * @return 生成二维码结果(true or false)
     */
    public static boolean encodeQRCodeImage(String content, String charset,String mediaType,OutputStream outputStream ,int width, int height) {
        Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
        if (charset == null) {
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");  //设置内容编码
        } else {
            hints.put(EncodeHintType.CHARACTER_SET, charset);  //设置内容编码
        }

        try {
            // 生成矩阵
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
            //生成的二维码图片默认背景为白色,前景为黑色,但是在加入logo图像后会导致logo也变为黑白色,至于是什么原因还未知
            //所以这里对其第一个参数黑色将ZXing默认的前景色0xFF000000稍微改了一下0xFF000001,最终效果也是白色背景黑色前景的二维码,且logo颜色保持原有不变
            MatrixToImageConfig config = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
            //这里要显式指定MatrixToImageConfig,否则还会按照默认处理将logo图像也变为黑白色(如果打算加logo的话,反之则不须传MatrixToImageConfig参数)
            MatrixToImageWriter.writeToStream(bitMatrix, mediaType, outputStream, config); //输出图像
            log.info("二维码生成成功：");
            return true;
        } catch (Exception e) {
            log.error("二维码生成失败");
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 生产二维码，并且加上logo图片。并把二维码放进流里面
     * @param content 内容
     * @param charset 编码
     * @param mediaType 后缀，如 png
     * @param outputStream 输出刘
     * @param width  宽度
     * @param height 高度
     * @param logoPath logo图片路径(web容器的相对路径)
     * @return
     */
    public static boolean encodeQRCodeImage(String content, String charset,String mediaType,OutputStream outputStream ,int width, int height,String logoPath) {
        Map<EncodeHintType, Object> hints = new HashMap<EncodeHintType, Object>();
        if (charset == null) {
            hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");  //设置内容编码
        } else {
            hints.put(EncodeHintType.CHARACTER_SET, charset);  //设置内容编码
        }
        try {
            // 生成矩阵
            BitMatrix bitMatrix = new MultiFormatWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints);
//            bitMatrix = deleteWhite(bitMatrix);
            //生成的二维码图片默认背景为白色,前景为黑色,但是在加入logo图像后会导致logo也变为黑白色,至于是什么原因还未知
            //所以这里对其第一个参数黑色将ZXing默认的前景色0xFF000000稍微改了一下0xFF000001,最终效果也是白色背景黑色前景的二维码,且logo颜色保持原有不变
            MatrixToImageConfig config = new MatrixToImageConfig(0xFF000001, 0xFFFFFFFF);
            //这里要显式指定MatrixToImageConfig,否则还会按照默认处理将logo图像也变为黑白色(如果打算加logo的话,反之则不须传MatrixToImageConfig参数)
            BufferedImage bufferedImage = MatrixToImageWriter.toBufferedImage(bitMatrix, config);
            String imgPath = PathUtil.getWrbRootPath() + logoPath;
            overlapImageToStream(bufferedImage, mediaType, imgPath, outputStream);
            log.info("二维码生成成功：");
            return true;
        } catch (Exception e) {
            log.error("二维码生成失败");
            e.printStackTrace();
        }
        return false;
    }


    /**
     * 解析二维码图片,待完整实现，测试解析已经可以做到了，之前解析不成功是因为 logo 图片占位太大了。 在加logo图片的时候 记得弄小点
     *
     * @param imagePath 二维码图片存放路径(含文件名)
     * @param charset   解码二维码内容时采用的字符集(传null时默认采用UTF-8编码)
     */
    public static void decodeQRCodeImage(String imagePath, String charset) {
        BufferedImage image;
        try {
            image = ImageIO.read(new File(imagePath));
            LuminanceSource source = new BufferedImageLuminanceSource(image);
            Binarizer binarizer = new HybridBinarizer(source);
            BinaryBitmap binaryBitmap = new BinaryBitmap(binarizer);
            Map<DecodeHintType, Object> hints = new HashMap<DecodeHintType, Object>();
            hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
            Result result = new MultiFormatReader().decode(binaryBitmap, hints);// 对图像进行解码
            System.out.println(result.getText());
            /*JSONObject content = JSONObject.parseObject(result.getText());
            System.out.println("图片中内容：	");
            System.out.println("author：	" + content.getString("author"));
            System.out.println("zxing：	" + content.getString("zxing"));
            System.out.println("图片中格式：	");
            System.out.println("encode：	" + result.getBarcodeFormat());*/
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        }

    }

    /**
     * 去除留白
     * @param matrix
     * @return
     */
    public static BitMatrix deleteWhite(BitMatrix matrix){
        int[] rec = matrix.getEnclosingRectangle();
        int resWidth = rec[2] + 1;
        int resHeight = rec[3] + 1;

        BitMatrix resMatrix = new BitMatrix(resWidth, resHeight);
        resMatrix.clear();
        for (int i = 0; i < resWidth; i++) {
            for (int j = 0; j < resHeight; j++) {
                if (matrix.get(i + rec[0], j + rec[1]))
                    resMatrix.set(i, j);
            }
        }
        return resMatrix;
    }

    /**
     * 自定义留白大小
     * @param matrix
     * @param margin
     * @return
     */
    public static BitMatrix updateBit(BitMatrix matrix, int margin){
        int tempM = margin*2;
        int[] rec = matrix.getEnclosingRectangle();   //获取二维码图案的属性
        int resWidth = rec[2] + tempM;
        int resHeight = rec[3] + tempM;
        BitMatrix resMatrix = new BitMatrix(resWidth, resHeight); // 按照自定义边框生成新的BitMatrix
        resMatrix.clear();
        for(int i= margin; i < resWidth- margin; i++){   //循环，将二维码图案绘制到新的bitMatrix中
            for(int j=margin; j < resHeight-margin; j++){
                if(matrix.get(i-margin + rec[0], j-margin + rec[1])){
                    resMatrix.set(i,j);
                }
            }
        }
        return resMatrix;
    }

    public static void main(String[] args) throws FileNotFoundException {
//        JSONObject json = new JSONObject();
//        json.put(
//                "zxing",
//                "https://github.com/zxing/zxing/tree/zxing-3.0.0/javase/src/main/java/com/google/zxing");
//        json.put("author", "shihy");
//        FileOutputStream fileOutputStream = new FileOutputStream(new File("C:\\Users\\win\\Desktop\\qrCodeImg\\1.png"));
//        encodeQRCodeImage(json.toString(), null,"png",fileOutputStream,500,500);

        String logoPath = "C:\\Users\\win\\Desktop\\createPayQrCode.png";
//        encodeQRCodeImage(json.toString(), null, "C:\\Users\\win\\Desktop\\qrCodeImg\\", "2.png", 500, 500, logoPath);
        decodeQRCodeImage(logoPath, null);
    }
}
